package myzzyl.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.iotda.v5.IoTDAClient;
import com.huaweicloud.sdk.iotda.v5.model.*;
import com.huaweicloud.sdk.iotda.v5.region.IoTDARegion;
import jakarta.validation.constraints.Size;
import lombok.extern.slf4j.Slf4j;
import myzzyl.constants.ErrorConstants;
import myzzyl.domain.dto.DeviceDto;
import myzzyl.domain.pojo.Device;
import myzzyl.domain.vo.DeviceVo;
import myzzyl.exception.BusinessException;
import myzzyl.response.PageResponse;
import myzzyl.service.DeviceService;
import myzzyl.mapper.DeviceMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.net.ConnectException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
* @author 33249
* @description 针对表【device】的数据库操作Service实现
* @createDate 2025-08-24 16:27:08
*/
@Service
@Slf4j
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device>
    implements DeviceService {


    @Autowired
    private IoTDAClient client;


    /**
     * 分页获取设备
     * @param pageNum 页码
     * @param pageSize 每页大小
     * @param deviceName 设备名称
     * @param productKey 产品id
     * @param locationType 设备类型
     * @return 返回值
     */
    @Override
    public PageResponse<Device> pageQuery(Integer pageNum, Integer pageSize, String deviceName, String productKey, Integer locationType) {
        // 创建分页对象
        Page<Device> page = new Page<>(pageNum, pageSize);

        // 添加排序字段
        page.addOrder(OrderItem.desc("create_time"));

        // 分页查询
        Page<Device> devicePage = lambdaQuery()
                .like(StrUtil.isNotBlank(deviceName), Device::getDeviceName, deviceName)
                .eq(StrUtil.isNotBlank(productKey), Device::getProductKey, productKey)
                .eq(locationType != null, Device::getLocationType, locationType)
                .page(page);


        // 构造基本分页响应式对象返回
        return new PageResponse<>(devicePage.getPages(), devicePage.getTotal(), devicePage.getRecords());
    }

    /**
     * 获取设备详情数据
     * @param deviceDto 设备表单请求封装类
     * @return 返回值
     */
    @Override
    public DeviceVo getInfo(DeviceDto deviceDto) {
        // 查询设备是否存在
        Device device = lambdaQuery().eq(Device::getIotId, deviceDto.getIotId()).one();
        if(device == null) {
            throw new BusinessException(ErrorConstants.DEVICE_IS_NOT_EXIST);
        }

        // 查询Iot上的设备详情信息
        ShowDeviceRequest request = new ShowDeviceRequest();
        request.withDeviceId(deviceDto.getIotId());
        ShowDeviceResponse response = null;
        try {
            response = client.showDevice(request);
            log.info("response:{}", response);
        } catch (Exception e) {
            throw new RuntimeException("IOT查询设备失败");
        }

        // 构造vo对象返回
        DeviceVo deviceVo = new DeviceVo();
        BeanUtil.copyProperties(device, deviceVo);
        deviceVo.setStatus(response.getStatus());
        deviceVo.setFirmwareVersion(response.getFwVersion());
        deviceVo.setNodeType(response.getNodeType());
        deviceVo.setRegion("北京四");
        deviceVo.setProductName(response.getProductName());

        // 获取激活时间
        String activeTimeStr = response.getActiveTime();
        // 判断是否为空
        if (StringUtils.isNotBlank(activeTimeStr)) {
            // 解析激活时间
            LocalDateTime activeTime = LocalDateTimeUtil.parse(activeTimeStr, DatePattern.UTC_MS_PATTERN);
            //时区转换
            activeTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                    .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                    .toLocalDateTime();
            deviceVo.setGmtActive(activeTime);
        }

        return deviceVo;
    }


    /**
     * 新增设备
     * @param deviceDto 设备表单请求封装类
     * @return 返回值
     */
    @Override
    public boolean add(DeviceDto deviceDto) {
        // 判断设备名称是否为空
        if (StrUtil.isBlank(deviceDto.getDeviceName())) {
            throw new BusinessException(ErrorConstants.DEVICE_NAME_IS_NULL);
        }

        // 判断设备标识码是否为空
        if (StrUtil.isBlank(deviceDto.getNodeId())) {
            throw new BusinessException(ErrorConstants.DEVICE_NODE_ID_IS_NULL);
        }

        // 查询设备名称是否重复
        if (lambdaQuery().eq(Device::getDeviceName, deviceDto.getDeviceName()).exists()) {
            throw new BusinessException(ErrorConstants.DEVICE_NAME_IS_ALREADY_EXIST);
        }

        // 查询设备标识码是否重复
        if (lambdaQuery().eq(Device::getNodeId, deviceDto.getNodeId()).exists()) {
            throw new BusinessException(ErrorConstants.DEVICE_NODE_ID_IS_ALREADY_EXIST);
        }

        // 查询设备是否已经绑定到了老人/位置
        if (lambdaQuery().eq(Device::getProductKey, deviceDto.getProductKey())
                         .eq(Device::getLocationType, deviceDto.getLocationType())
                         .eq(Device::getPhysicalLocationType, deviceDto.getPhysicalLocationType())
                         .eq(Device::getBindingLocation, deviceDto.getBindingLocation()).exists()) {
            throw new BusinessException(ErrorConstants.DEVICE_IS_BIND_TO_ELDER_OR_POSITION);
        }


        // 创建设备
        AddDeviceRequest request = new AddDeviceRequest();
        AddDevice body = new AddDevice();
        AuthInfo authInfo = new AuthInfo();

        // 利用uuid随机生成秘钥
        String secret = UUID.randomUUID().toString().replaceAll("-", "");
        authInfo.withSecret(secret);

        body.withAuthInfo(authInfo);
        body.withProductId(deviceDto.getProductKey());
        body.withDeviceName(deviceDto.getDeviceName());
        body.withNodeId(deviceDto.getNodeId());
        request.withBody(body);

        try {
            AddDeviceResponse response = client.addDevice(request);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("IOT新增设备失败");
        }

        // 新增设备项目
        Device device = new Device();
        BeanUtils.copyProperties(deviceDto, device);
        device.setIotId(deviceDto.getProductKey() + "_" + device.getNodeId());
        device.setSecret(secret);

        return save(device);
    }


    /**
     * 编辑设备
     * @param deviceDto 设备表单请求封装类
     * @return 返回值
     */
    @Override
    public boolean edit(DeviceDto deviceDto) {
        // 判断设备标识码是否为空
        if (StrUtil.isBlank(deviceDto.getNodeId())) {
            throw new BusinessException(ErrorConstants.DEVICE_NODE_ID_IS_NULL);
        }
        // 查询设备标识码是否重复（除去自己）
        if (lambdaQuery().eq(Device::getNodeId, deviceDto.getNodeId())
                         .ne(Device::getId, deviceDto.getId()).exists()) {
            throw new BusinessException(ErrorConstants.DEVICE_NODE_ID_IS_ALREADY_EXIST);
        }

        // todo 绑定位置所上报的数据清空；

        // 更新设备
        Device device = new Device();
        BeanUtils.copyProperties(deviceDto, device);
        return updateById(device);
    }


    /**
     * 删除设备
     * @param deviceDto 设备表单请求封装类
     * @return 返回值
     */
    @Override
    public boolean remove(DeviceDto deviceDto) {
        // 查询设备是否存在
        Device device = lambdaQuery().eq(Device::getIotId, deviceDto.getIotId()).one();
        if(device == null) {
            throw new BusinessException(ErrorConstants.DEVICE_IS_NOT_EXIST);
        }

        DeleteDeviceRequest request = new DeleteDeviceRequest();
        request.withDeviceId(deviceDto.getIotId());

        try {
            DeleteDeviceResponse response = client.deleteDevice(request);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("IOT删除设备失败");
        }

        // 删除设备
        return baseMapper.deleteById(device.getId()) > 0;
    }


    /**
     * 查看物模型运行状态卡片
     * @param deviceDto 设备表单请求封装类
     * @return 返回值
     */
    @Override
    public List<DeviceVo> QueryDevicePropertyStatus(DeviceDto deviceDto) {
        // 查询IOT平台上设备最新上报数据
        ShowDeviceShadowRequest request = new ShowDeviceShadowRequest();
        request.withDeviceId(deviceDto.getIotId());

        ShowDeviceShadowResponse response;
        try {
            response = client.showDeviceShadow(request);
            log.info("IOT设备最新上报数据:{}", response);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("IOT设备最新上报数据查询失败");
        }

        // 存储返回结果
        List<DeviceVo> deviceVoList = new ArrayList<>();

        // 判断设备影子信息是否为空
        if (CollectionUtil.isEmpty(response.getShadow())) {
            return deviceVoList;
        }

        // 获取设备最近一次上报的属性影子数据
        DeviceShadowProperties shadowProperties = response.getShadow().get(0).getReported();
        // 获取上报时间
        String eventTimeStr = shadowProperties.getEventTime();
        // 解析上报时间
        LocalDateTime activeTime = LocalDateTimeUtil.parse(eventTimeStr, "yyyyMMdd'T'HHmmss'Z'");
        //时区转换
        LocalDateTime eventTime = activeTime.atZone(ZoneId.from(ZoneOffset.UTC))
                .withZoneSameInstant(ZoneId.of("Asia/Shanghai"))
                .toLocalDateTime();

        // 获取属性并解析为json对象
        Object properties = shadowProperties.getProperties();
        JSONObject jsonObject = JSONUtil.parseObj(properties);

        // 循环遍历(k,v) 设置属性
        jsonObject.forEach((k, v) -> {
            DeviceVo deviceVo = new DeviceVo();
            deviceVo.setFunctionId(k);
            deviceVo.setValue(v);
            deviceVo.setEventTime(eventTime);

            deviceVoList.add(deviceVo);
        });

        // 返回储存返回对象的集合
        return deviceVoList;
    }



}
